﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
using Common.Extensions;

namespace ContactManagement.AppCode.HtmlHelperForViews
{
    /// <summary>
    /// HTML-Helper der das passende Paging Element erstellt, für die aktuelle Tabelle.
    /// </summary>
    public class PagingHtmlHelper
    {
        #region Member
        private readonly HtmlHelper CurrentHtmlHelper;
        
        /// <summary>
        /// Die Pagesize.
        /// </summary>
        private int PageSize { get; set; }

        /// <summary>
        /// Der Aktuelle PageIndex
        /// </summary>
        private int CurrentPageIndex { get; set; }
        #endregion

        #region Konstruktor
        /// <summary>
        /// Konstruktor der HTML Helpers Klasse für die Kategorien. die Statische Klasse zum Instanzieieren findet man in den HtmlHelperExtensions
        /// </summary>
        public PagingHtmlHelper(HtmlHelper htmlHelper)
        {
            CurrentHtmlHelper = htmlHelper;
            PageSize = WebConstants.PageSizePaging;
            //Den aktuelle PageIndex anhand der URL Parameter ermitteln
            CurrentPageIndex = PagingExtensions.CurrentPageIndex();
        }
        #endregion

        #region Public Functions
        /// <summary>
        /// Erstellen des passenden Paging HTML-Helper
        /// </summary>
        /// <param name="source">Auflistung für die das Paging Element erstellt werden soll</param>
        /// <returns>Paging Element für die übergebenen Source</returns>
        public MvcHtmlString Create<T>(IEnumerable<T> source)
        {
            //Anzahl der Pages in der Collection, ist abhängig von der PageSize
            double pageCount = source.ToPagedList(CurrentPageIndex, PageSize).PageCount;

            //Nur das Paging anzeigen, wenn die Anzahl der Einträge größer der Pagesize ist
            if (source.Count() > PageSize)
            {
                //Ermitteln des aktuellen URL Strings, hier gibt es einen Parameter {0} der mit dem PageIndex ersetzt werden kann.
                string queryString = BuildUrlPagingString();
                int showPages = 9;

                //Stringbuilder der den HTML Code für das Paging Element enthält.
                StringBuilder paging = new StringBuilder();
                paging.Append("<ul class=\"pagination-clean\">");
               
                //Einen Link für die Previous Page hinzufügen.
                paging.Append(AddPreviousPage(source, queryString));


                //Festlegen ab welcher Startseit das Paging element angezeigt werden soll
                int startPage = 0;
                if (CurrentPageIndex > showPages)
                {
                    startPage = CurrentPageIndex - showPages;
                }
                
                //Erstellen der Seitenzahlen für die gefundenen Suchergebnisse
                for (int i = startPage; i < pageCount; i++)
                {
                    //Abbruch Bedingung, wann keine Paging elemente mehr erstellt werden sollen
                    if ((i > showPages && i > (CurrentPageIndex + showPages)))
                    {
                      break;
                    }

                    //Das Passende Paging Element erstellen
                    AddPagingIndex(i, queryString, paging);
                }

                //Den Next button erstellen für das Paging.
                paging.Append(AddNextPage(source, queryString));

                //Hinzufügen der Paging Zusammenfassung
                paging.Append(AddSummary(source));

                paging.Append("</ul>");
                return MvcHtmlString.Create(paging.ToString());
            }

            //Wenn keine Einträge in der Liste enhalten sind, dann auch kein Paging Element erstellen.
            return MvcHtmlString.Empty;
        }

        #endregion

        #region Private Functions
        /// <summary>
        /// Den aktuellen PagingIndex der Auflistung hinzufügen
        /// </summary>
        /// <param name="index">Der Pagindex für den das Paging element erstellt werden soll</param>
        /// <param name="queryString">der Querystring der verwendet werden soll</param>
        /// <param name="paging">Der StringBuilder dem das Paging Element angehägt werden soll</param>
        private void AddPagingIndex(int index, string  queryString, StringBuilder paging)
        {
            paging.Append("<li class=\"pageIndex\">");
            paging.Append("<a href=\"").Append(string.Format(queryString, index)).Append("\">");
            //Prüfen ob es sich um die aktuelle Seite handelt die angezeigt werden soll, diese wird Fett dargestellt.
            if (index == CurrentPageIndex)
            {
                paging.Append("<b>").Append((index + 1).ToString()).Append("</b></a>");
            }
            else
            {
                paging.Append((index + 1).ToString()).Append("</a>");
            }
            paging.Append("</li>");
        }

        /// <summary>
        /// Gibt die zusammenfassung zurück für das Paging z.B. 1/23 (236)
        /// </summary>
        /// <param name="source">Die Liste die angezeigt wird.</param>
        private string AddSummary<T>(IEnumerable<T>source)
        {
            StringBuilder summary = new StringBuilder();
            //Zusammenfassung Anzeigen wieviele Seiten insegesamt zur Verfügung stehen
            summary.Append("<li class=\"summaryPaging\">");
            summary.Append(string.Format("{0}/{1} ({2})", CurrentPageIndex + 1, source.ToPagedList(CurrentPageIndex, PageSize).PageCount, source.Count()));
            summary.Append("</li>");
            return summary.ToString();
        }

        /// <summary>
        /// Erstellt einen Prevouis Link für die aktuelle Collection
        /// </summary>
        /// <param name="queryString">der Link für die Seite auf die verwiesen wird.</param>
        private string AddPreviousPage<T>(IEnumerable<T> source, string queryString)
        {
            StringBuilder previous = new StringBuilder();

            //Wenn es eine Vorherige Seite gibt, Previous anzeigen
            if (source.ToPagedList(CurrentPageIndex, PageSize).HasPreviousPage)
            {
                previous.Append("<li class=\"previousPage\">");
                previous.Append("<a href=").Append(string.Format(queryString, CurrentPageIndex - 1)).Append(">");
                previous.Append(App_GlobalResources.GlobalStrings.PagingPreviousRESX).Append("</a>");
                previous.Append("</li>");
            }
            else
            {
                //Nur den Zurück Button ohne Link anzeigen
                previous.Append("<li class=\"previousPage\">");
                previous.Append(App_GlobalResources.GlobalStrings.PagingPreviousRESX);
                previous.Append("</li>");
            }

            return previous.ToString();
        }

        /// <summary>
        /// Erstellt einen Next Link für die aktuelle Collection
        /// </summary>
        /// <param name="source">Die Auflistung</param>
        /// <param name="queryString">Die URL in der nur noch der Index gesetzt werden muss für {0}</param>
        private string AddNextPage<T>(IEnumerable<T> source, string queryString)
        {
            StringBuilder nexPage = new StringBuilder();

            //Wenn es eine nächste Seite gibt, den Next Button anzeigen
            if (source.ToPagedList(CurrentPageIndex, WebConstants.PageSizePaging).HasNextPage)
            {
                nexPage.Append("<li class=\"nextPage\">");
                nexPage.Append("<a href=").Append(string.Format(queryString, CurrentPageIndex + 1)).Append(">");
                nexPage.Append(App_GlobalResources.GlobalStrings.PagingNextRESX).Append("</a>");
                nexPage.Append("</li>");
            }
            else
            {
                //nur den Next button anzeigen ohne Link
                nexPage.Append("<li class=\"nextPage\">");
                nexPage.Append(App_GlobalResources.GlobalStrings.PagingNextRESX);
                nexPage.Append("</li>");
            }

            return nexPage.ToString();
        }

        /// <summary>
        /// Erstellen des passenden URL Paging Links. Anhand der aktuellen Url und Query Strings
        /// </summary>
        /// <returns>String mit der passenden URL. Der PageIndex wird durch ein "{0}" gesetzt, welcher dann einfach in einer string.format Anweisung ersetzt werden kann.</returns>
        private string BuildUrlPagingString()
        {
            StringBuilder url = new StringBuilder();
            //Zusammenbauen der Basis URL - Controller und Action die gerade angezeigt wird.
            foreach (KeyValuePair<string, object> eUrlValues in CurrentHtmlHelper.ViewContext.RouteData.Values)
            {
                url.Append("/").Append(eUrlValues.Value.ToString());
            }

            string urlQueryParams = string.Empty;
            bool pagingAppended = false;

            //ermitteln ob in der URL bereits Query Parameter angezeigt werden.
            if (CurrentHtmlHelper.ViewContext.HttpContext.Request.Url != null && !string.IsNullOrEmpty(CurrentHtmlHelper.ViewContext.HttpContext.Request.Url.Query))
            {
                StringBuilder newQuery = new StringBuilder();
                //Den aktuellen QueryString ermitteln
                string query = CurrentHtmlHelper.ViewContext.HttpContext.Request.Url.Query;
                //Alle einzelqueries ermitteln, damit diese überprüft werden können ob es sich um die aktuelle page handelt.
                string[] queries = query.Split('&');

                int i = 0;
                //Alle Queries durchgehen
                foreach (string eQuery in queries)
                {
                    //Prüfen ob es sich um den ersten Eintrag handelt und wenn noch kein Eintrag existiert "?" hinzufügen
                    if (newQuery.Length == 0 && (!eQuery.StartsWith("?") || eQuery.Contains(string.Format("{0}=", PagingExtensions.UrlPage))))
                    {
                        newQuery.Append("?");
                    }

                    //Wenn es sich um den 2. oder einen höheren Eintrag handelt das "&" als Trennzeichen zwischen den Queries wählen
                    if (i > 0 )
                    {
                        newQuery.Append("&");
                    }

                    //Handelt es sich um den aktuellen Page Eintrag, dann wird dieser durch einen neuen Ersetzt.
                    if (eQuery.Contains(string.Format("{0}=", PagingExtensions.UrlPage)))
                    {
                        //den String für "String.Format" vorbereiten, damit nur noch der passende PageIndex eingetragen werden muss.
                        newQuery.Append(PagingExtensions.UrlPage + "={0}");
                        pagingAppended = true;
                    }
                    else
                    {
                        //Sonst den originalen Query Eintrag dem QueryString hinzufügen wie z.B. Sortierung
                        newQuery.Append(eQuery);
                    }
                    
                    i++;
                }

                urlQueryParams = newQuery.ToString();
            }

            //Prüfen ob bereits ein Page eintrag hinzugefügt wurde.
            if (!pagingAppended)
            {
                if (urlQueryParams.Length == 0)
                {
                    //Wenn keine url Query Parameter vorhanden sind einen neuen für die Page erstellen
                    urlQueryParams += "?" + PagingExtensions.UrlPage +"={0}";          
                }
                else
                {
                    urlQueryParams += "&" + PagingExtensions.UrlPage + "={0}";          
                }
            }

            //die Query Parameter inkl. {0} wo der Index eingetragen werden kann.
            return url.Append(urlQueryParams).ToString();
        }
        #endregion
    }
}